//
// Created by 李传智 on 2018/4/14.
//

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "SLAudioPlay.h"
#include "CodecParameter.h"
#include "Logger.h"

static SLObjectItf slObject = NULL;
// 混音器
static SLObjectItf slMixObject = NULL;
// 播放器
static SLObjectItf slPlayerObject = NULL;
static SLPlayItf slPlayer = NULL;
// 缓冲队列
static SLAndroidSimpleBufferQueueItf slBuffer = NULL;

// 播放回调
static void callback(SLAndroidSimpleBufferQueueItf bufferQueue, void *context) {
    SLAudioPlay *audioPlay = (SLAudioPlay *) context;
    if (!audioPlay) {
        LOG_E("callback failed, context(SLAudioPlay) is NULL");
        return;
    }
    audioPlay->playCallback((void *) bufferQueue);
}

SLAudioPlay::SLAudioPlay() {
    buf = new unsigned char[1024 * 1024];
}

bool SLAudioPlay::startPlay(CodecParameter out) {

    close();

    mutex.lock();
    // OpenSLES
    SLresult slResult;
    SLEngineItf slEngine;
    SLDataLocator_OutputMix slDataLocatorMix;
    SLDataSink slDataSink;
    SLDataLocator_AndroidSimpleBufferQueue slDataLocatorBuffer;
    SLDataFormat_PCM slDataFormat;
    SLDataSource slDataSource;
    SLInterfaceID slPlayerIDs[1] = {SL_IID_BUFFERQUEUE};
    SLboolean slPlayerFlags[1] = {SL_BOOLEAN_FALSE};

    // 使用 OpenSLES 播放音频
    slResult = slCreateEngine(&slObject, 0, 0, 0, 0, 0);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slCreateEngine failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slObject)->Realize(slObject, SL_BOOLEAN_FALSE);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slObject Realize failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slObject)->GetInterface(slObject, SL_IID_ENGINE, &slEngine);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slObject GetInterface SL_IID_ENGINE failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    // 初始化混音器
    slResult = (*slEngine)->CreateOutputMix(slEngine, &slMixObject, 0, 0, 0);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slEngine CreateOutputMix failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slMixObject)->Realize(slMixObject, SL_BOOLEAN_FALSE);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slMix Realize failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slDataLocatorMix = {SL_DATALOCATOR_OUTPUTMIX, slMixObject};
    slDataSink = {&slDataLocatorMix, 0};
    slDataLocatorBuffer = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
    slDataFormat = {
            SL_DATAFORMAT_PCM,
            (SLuint32) out.channels,  // 双声道
            (SLuint32) out.sampleRate * 1000, // 44100 hz
            SL_PCMSAMPLEFORMAT_FIXED_16, // 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, // 立体声
            SL_BYTEORDER_LITTLEENDIAN // 字节序 小端
    };
    slDataSource = {&slDataLocatorBuffer, &slDataFormat};
    // 初始化播放器
    slResult = (*slEngine)->CreateAudioPlayer(slEngine,
                                              &slPlayerObject,
                                              &slDataSource,
                                              &slDataSink,
                                              1,
                                              slPlayerIDs,
                                              slPlayerFlags);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slEngine CreateAudioPlayer failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slPlayerObject)->Realize(slPlayerObject, SL_BOOLEAN_FALSE);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slPlayerObject Realize failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slPlayerObject)->GetInterface(slPlayerObject, SL_IID_PLAY, &slPlayer);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slPlayerObject GetInterface SL_IID_PLAY failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slPlayerObject)->GetInterface(slPlayerObject, SL_IID_BUFFERQUEUE, &slBuffer);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slPlayerObject GetInterface SL_IID_BUFFERQUEUE failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    slResult = (*slBuffer)->RegisterCallback(slBuffer, callback, this);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slBuffer RegisterCallback failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }
    // 设置播放状态
    slResult = (*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PLAYING);
    if (slResult != SL_RESULT_SUCCESS) {
        LOG_E("slPlayer SetPlayState failed, error = %d", slResult);
        mutex.unlock();
        return false;
    }

    (*slBuffer)->Enqueue(slBuffer, "", 1);

    isExist = false;

    LOG_I("SLAudioPlay startPlay success");
    mutex.unlock();

    return true;
}

void SLAudioPlay::close() {
    IAudioPlay::clear();
    mutex.lock();
    if (slPlayer && (*slPlayer)) {
        (*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_STOPPED);
    }
    if (slBuffer && (*slBuffer)) {
        (*slBuffer)->Clear(slBuffer);
    }
    if (slPlayerObject && (*slPlayerObject)) {
        (*slPlayerObject)->Destroy(slPlayerObject);
    }
    if (slMixObject && (*slMixObject)) {
        (*slMixObject)->Destroy(slMixObject);
    }
    if (slObject && (*slObject)) {
        (*slObject)->Destroy(slObject);
    }
    slObject = NULL;
    slMixObject = NULL;
    slPlayerObject = NULL;
    slPlayer = NULL;
    slBuffer = NULL;
    mutex.unlock();
}

void SLAudioPlay::playCallback(void *_bufferQueue) {
    if (!buf) {
        LOG_E("SLAudioPlay::playCallback failed, buf is NULL");
        return;
    }
    VideoData data = getVideoData();
    if (data.size <= 0) {
        return;
    }
    memcpy(buf, data.data, (size_t) data.size);
    mutex.lock();
    if (slBuffer && *slBuffer) {
        (*slBuffer)->Enqueue(slBuffer, buf, (size_t) data.size);
    }
    mutex.unlock();
    data.free();
}

SLAudioPlay::~SLAudioPlay() {
    delete buf;
    buf = 0;
}